//
//  FFmpegDecoder.m
//  ffmpegTest
//
//  Created by WZ on 2017/9/14.
//  Copyright © 2017年 WZ. All rights reserved.
//


#import "FFmpegDecoder.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"

@interface FFmpegDecoder ()
{
    AVFormatContext *formatCtx;
    int             videoStreamIndex;
    AVCodecContext  *codecCtx;
    AVCodec         *codec;
    AVFrame         *frame;
    int             videoW;
    int             videoH;
}

@end

@implementation FFmpegDecoder

- (instancetype)initWithMoviePath:(NSString *)path {
    if (self = [super init]) {
        //初始化操作
        [self prepareDecodeWithPath:path];
    }
    return self;
}

- (void)prepareDecodeWithPath:(NSString *)path {

    //1.注册所有容器和解码器
    av_register_all();
    
    //2.判断地址是否是网络串流
    BOOL isNetwork = NO;
    if(![path containsString:@":"]) {
        isNetwork = NO;
    } else {
        if ([[path componentsSeparatedByString:@":"].firstObject isEqualToString:@"file"]) {
            isNetwork = NO;
        } else {
            isNetwork = YES; //其他所有的rtsp/rtmp/http开头都认为是网络串流
        }
    }
    
    if (isNetwork) { //在某条件出现时做某事，不出现该条件时不做任何处理，此种情况只需要if，不需要else
        avformat_network_init();
    }
    
    //3.打开文件，给formatCtx赋值
    formatCtx = avformat_alloc_context();
    avformat_open_input(&formatCtx, [path cStringUsingEncoding:NSUTF8StringEncoding], NULL, NULL);
    
    //4.查找path文件中所有的streams，存入formatCtx中
    if (avformat_find_stream_info(formatCtx, NULL) < 0) {
        NSLog(@"传入的path中没有找到任何1个stream");
        return;
    }

    //5.从formatCtx的nb_streams中找到video视频流
    for (int i = 0; i < formatCtx->nb_streams; i++) {
        if (formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;
        }
    }

    //6.找到视频流index后，创建视频流的解码上下文
    codecCtx = formatCtx->streams[videoStreamIndex]->codec; //此处codec类型为AVCodecContext*类型,按esc可查看，不知为何不写作codecCtx;
    
    //7.根据codecCtx->codec_id找到对于的解码器codec
    //AVCodec *codec = codecCtx->codec;
    AVCodec *codec1 = avcodec_find_decoder(codecCtx->codec_id);
    codec = codec1;
    
    //8.打开解码器
    avcodec_open2(codecCtx, codec, NULL);
    
    //创建解码后的frame，以备用
    frame = av_frame_alloc();
    videoW = codecCtx->width;
    videoH = codecCtx->height;

}


- (void)readPacketAndDecodeIntoFrame {
    
    AVPacket packet;
    int decodeThisFrameFinished = 0;
    
    //将formatCtx环境中的数据读取到packet中
    while (!decodeThisFrameFinished && av_read_frame(formatCtx, &packet) == 0) {
        if (packet.stream_index == videoStreamIndex) {
            
            //真正的在解码这个packet，将产生的数据存入frame中
            avcodec_decode_video2(codecCtx, frame, &decodeThisFrameFinished, &packet);
            
            //解码后得到AVFrame结构体，分析如下
            /*
             
             uint8_t *data：解码后原始数据（对视频来说是YUV，RGB，对音频来说是PCM）
             int linesize：data中“一行”数据的大小。注意：未必等于图像的宽，一般大于图像的宽。
             int width, height：视频帧宽和高（1920x1080,1280x720...）
             int nb_samples：音频的一个AVFrame中可能包含多个音频帧，在此标记包含了几个
             int format：解码后原始数据类型（YUV420，YUV422，RGB24...）
             int key_frame：是否是关键帧
             enum AVPictureType pict_type：帧类型（I,B,P...）
             AVRational sample_aspect_ratio：宽高比（16:9，4:3...）
        
             */
        }

    }

}


/* 以下可将 AVFrame.data 转为 AVPicture.data 在转为 CGImage 最后转为 UIImage 显示
 或者直接使用OpenGL渲染YUV也可以
 
-(void)setupScaler {
    
    // Release old picture and scaler
    avpicture_free(&picture);
    sws_freeContext(img_convert_ctx);
    
    // Allocate RGB picture
    avpicture_alloc(&picture, PIX_FMT_RGB24, outputWidth, outputHeight);
    
    // Setup scaler
    static int sws_flags =  SWS_FAST_BILINEAR;
    img_convert_ctx = sws_getContext(pCodecCtx->width,
                                     pCodecCtx->height,
                                     pCodecCtx->pix_fmt,
                                     outputWidth,
                                     outputHeight,
                                     PIX_FMT_RGB24,
                                     sws_flags, NULL, NULL, NULL);
    
}

-(void)convertFrameToRGB {
    sws_scale (img_convert_ctx, pFrame->data, pFrame->linesize,
               0, pCodecCtx->height,
               picture.data, picture.linesize);
}

-(UIImage *)imageFromAVPicture:(AVPicture)pict width:(int)width height:(int)height {
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pict.data[0], pict.linesize[0]*height,kCFAllocatorNull);
    CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGImageRef cgImage = CGImageCreate(width,
                                       height,
                                       8,
                                       24,
                                       pict.linesize[0],
                                       colorSpace,
                                       bitmapInfo,
                                       provider,
                                       NULL,
                                       NO,
                                       kCGRenderingIntentDefault);
    CGColorSpaceRelease(colorSpace);
    UIImage *image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);
    CGDataProviderRelease(provider);
    CFRelease(data);
    
    return image;
}
 
*/

@end
